﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
using System.IO;
using iPhoneTrackerWin.Data;

namespace iPhoneTrackerWin.Parser
{
    class GPXLoader
    {
        /// <summary>
        /// Erzeugt einen neuen GPXLoader
        /// </summary>
        public GPXLoader()
        {
            //TODO: füge User und File ein
        }

        /// <summary> 
        /// Load the Xml document for parsing 
        /// </summary> 
        /// <param name="sFile">Fully qualified file name (local)</param> 
        /// <returns>XDocument</returns> 
        public XDocument LoadGPXFromFile(string sFile)
        {
            FileStream fs = new FileStream(sFile, FileMode.Open);
            XDocument gpxDoc = XDocument.Load(fs);
            return gpxDoc; 
        }

        /// <summary>
        /// Parses the given gpxString into a XDocument
        /// </summary>
        /// <param name="gpxString"></param>
        /// <returns></returns>
        public XDocument LoadGPXFromString(string gpxString)
        {
            XDocument gpxDoc = XDocument.Parse(gpxString);
            return gpxDoc;
        }

        /// <summary> 
        /// Load the namespace for a standard GPX document 
        /// </summary> 
        /// <returns></returns> 
        private XNamespace GetGpxNameSpace() 
        { 
            XNamespace gpx = XNamespace.Get("http://www.topografix.com/GPX/1/1"); 
            return gpx; 
        } 

        /// <summary> 
        /// When passed a file, open it and parse all waypoints from it. 
        /// </summary> 
        /// <param name="sFile">Fully qualified file name (local)</param> 
        /// <returns>string containing line delimited waypoints from 
        /// the file (for test)</returns> 
        /// <remarks>Normally, this would be used to populate the 
        /// appropriate object model</remarks> 
        public string LoadGPXWaypoints(XDocument gpxDoc) 
        { 
            XNamespace gpx = GetGpxNameSpace();

            var waypoints = from waypoint in gpxDoc.Descendants(gpx + "wpt") 
                    select new 
                    { 
                        Latitude = waypoint.Attribute("lat").Value, 
                        Longitude = waypoint.Attribute("lon").Value, 
                        Elevation = waypoint.Element(gpx + "ele") != null ? 
                            waypoint.Element(gpx + "ele").Value : null, 
                        Name = waypoint.Element(gpx + "name") != null ? 
                            waypoint.Element(gpx + "name").Value : null,
                        Dt = waypoint.Element(gpx + "time") != null ?
                            waypoint.Element(gpx + "time").Value : null 
                    };

            StringBuilder sb = new StringBuilder(); 
            foreach (var wpt in waypoints) 
            { 
                // This is where we'd instantiate data 
                // containers for the information retrieved. 
                sb.Append( 
                    string.Format("Name:{0} Latitude:{1} Longitude:{2} Elevation:{3} Date:{4}\n", 
                    wpt.Name,wpt.Latitude,wpt.Longitude, 
                    wpt.Elevation, wpt.Dt)); 
            } 

            return sb.ToString(); 
        }

        /// <summary>
        /// Liefert eine Liste von Waypoints von diesem XDocument
        /// </summary>
        /// <param name="gpxDoc"></param>
        /// <returns></returns>
        public List<GPS> getGPXWaypoints(XDocument gpxDoc)
        {
            XNamespace gpx = GetGpxNameSpace();

            //Parse aus Datei
            var points = from point in gpxDoc.Descendants(gpx + "wpt")
                select new
                {
                    Latitude = point.Attribute("lat").Value,
                    Longitude = point.Attribute("lon").Value,
                    Elevation = point.Element(gpx + "ele") != null ?
                    point.Element(gpx + "ele").Value : null,
                    Time = point.Element(gpx + "time") != null ?
                    point.Element(gpx + "time").Value : null
                };

            //Bau Objekt auf
            List<GPS> pointList = new List<GPS>();

            foreach (var p in points)
            {
                //Erzeuge einen GPS Punkt
                GPS point = new GPS();

                point.Lat = (float)double.Parse(p.Latitude.Replace(",", "."), System.Globalization.CultureInfo.InvariantCulture);
                point.Lon = (float)double.Parse(p.Longitude.Replace(",", "."), System.Globalization.CultureInfo.InvariantCulture);
                point.Height = (float)double.Parse(p.Elevation.Replace(",", "."), System.Globalization.CultureInfo.InvariantCulture);
                point.Timestamp = Convert.ToDateTime(p.Time);
                point.Type = (int)GPXPointType.Routepoint;

                pointList.Add(point);
            }

            return pointList;
        }

        /// <summary>
        /// Liefert eine Liste von Routen von diesem XDocument
        /// </summary>
        /// <param name="gpxDoc"></param>
        /// <returns></returns>
        public List<Route> getGPXRoutes(XDocument gpxDoc)
        {
            XNamespace gpx = GetGpxNameSpace();

            //Parse aus Datei
            var routes = from route in gpxDoc.Descendants(gpx + "rte")
                select new
                {
                    Points = (
                        from routepoint in route.Descendants(gpx + "rtept")
                        select new
                        {
                            Latitude = routepoint.Attribute("lat").Value,
                            Longitude = routepoint.Attribute("lon").Value,
                            Elevation = routepoint.Element(gpx + "ele") != null ?
                            routepoint.Element(gpx + "ele").Value : null,
                            Time = routepoint.Element(gpx + "time") != null ?
                            routepoint.Element(gpx + "time").Value : null
                        }
                    )
                };

            //Bau Objekt auf
            List<Route> routeList = new List<Route>();

            foreach (var rte in routes)
            {
                Route r = new Route();

                foreach (var p in rte.Points)
                {
                    //Erzeuge einen GPS Punkt
                    GPS point = new GPS();
                    point.Lat = (float)Convert.ToDouble(p.Latitude.Replace(",", "."), System.Globalization.CultureInfo.InvariantCulture);
                    point.Lon = (float)Convert.ToDouble(p.Longitude.Replace(",", "."), System.Globalization.CultureInfo.InvariantCulture);
                    point.Height = (float)Convert.ToDouble(p.Elevation.Replace(",", "."), System.Globalization.CultureInfo.InvariantCulture);
                    point.Timestamp = Convert.ToDateTime(p.Time);
                    point.Type = (int)GPXPointType.Routepoint;

                    //TODO: User und File hinzufügen
                    r.GPS.Add(point);
                }
                routeList.Add(r);
            }

            return routeList;
        }

        /// <summary>
        /// Liefert eine Liste von Tracks von diesem XDocument
        /// </summary>
        /// <param name="gpxDoc"></param>
        /// <returns></returns>
        public List<Track> getGPXTracks(XDocument gpxDoc) 
        { 
            XNamespace gpx = GetGpxNameSpace(); 

            //Parse aus Datei
            var tracks = from track in gpxDoc.Descendants(gpx + "trk") 
                    select new 
                    { 
                        Name = track.Element(gpx + "name") != null ? 
                        track.Element(gpx + "name").Value : null, 
                        Segs = ( 
                            from trackseg in track.Descendants(gpx + "trkseg")
                            select new
                            {
                                Points = (
                                    from trackpoint in track.Descendants(gpx + "trkpt") 
                                    select new 
                                    { 
                                        Latitude = trackpoint.Attribute("lat").Value, 
                                        Longitude = trackpoint.Attribute("lon").Value, 
                                        Elevation = trackpoint.Element(gpx + "ele") != null ? 
                                        trackpoint.Element(gpx + "ele").Value : null, 
                                        Time = trackpoint.Element(gpx + "time") != null ? 
                                        trackpoint.Element(gpx + "time").Value : null 
                                    } 
                                )
                            }
                        )
                    };

            //Bau Objekt auf
            List<Track> trackList = new List<Track>();
            
            foreach (var trk in tracks) 
            {
                Track t = new Track();

                foreach (var trkSeg in trk.Segs) 
                {
                    TrackSegment ts = new TrackSegment();
                    t.TrackSegment.Add(ts);

                    foreach (var trkPoint in trkSeg.Points)
                    {
                        //Erzeuge einen GPS Punkt
                        GPS point = new GPS();

                        point.Lat = (float)double.Parse(trkPoint.Latitude.Replace(",", "."), System.Globalization.CultureInfo.InvariantCulture);
                        point.Lon = (float)double.Parse(trkPoint.Longitude.Replace(",", "."), System.Globalization.CultureInfo.InvariantCulture);
                        point.Height = (float)double.Parse(trkPoint.Elevation.Replace(",", "."), System.Globalization.CultureInfo.InvariantCulture);
                        point.Timestamp = Convert.ToDateTime(trkPoint.Time);
                        point.Type = (int)GPXPointType.Trackpoint;

                        //TODO: User und File hinzufügen
                        ts.GPS.Add(point);
                    }
                }

                trackList.Add(t);
            }
            return trackList;
        }

        /// <summary>
        /// Lädt die Daten den angegebenen XDocument in ein GPXObjekt
        /// </summary>
        /// <param name="gpxDoc"></param>
        /// <returns></returns>
        public GPXObject LoadGPXObject(XDocument gpxDoc)
        {
            GPXObject gpxObject = new GPXObject();

            //Speicher alle Tracks
            foreach (Track t in getGPXTracks(gpxDoc))
                gpxObject.addTrack(t);

            //Speicher alle Routen
            foreach (Route r in getGPXRoutes(gpxDoc))
                gpxObject.addRoute(r);

            //Speicher alle Waypoints
            foreach (GPS p in getGPXWaypoints(gpxDoc))
                gpxObject.addPoint(p);

            return gpxObject;
        }
    }
}
